package com.bawei.es.service.impl;

import com.alibaba.fastjson.JSON;
import com.bawei.common.core.domain.R;
import com.bawei.common.core.exception.ServiceException;
import com.bawei.common.core.utils.StringUtils;
import com.bawei.es.service.EsService;
import com.bawei.mall.product.domain.MallProductInfo;
import com.bawei.system.domain.vo.IndexInfo;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageInfo;
import lombok.extern.log4j.Log4j;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.*;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;

/**
 * @Author：sxq
 * @Package：com.bawei.es.service.impl
 * @Project：mall_cloud
 * @name：EsServiceImpl
 * @Date：2024/6/20 11:26
 */
@Service
@Slf4j
public class EsServiceImpl implements EsService{


    @Autowired
    private RestHighLevelClient restHighLevelClient;


    private static  final String INDEX_NAME="product_info";

    @Override
    public R<Boolean> indexCreate(IndexInfo indexInfo) {

        if (StringUtils.isEmpty(indexInfo.getIndexName()) ||
                StringUtils.isEmpty(indexInfo.getIndexName())) {
            throw new ServiceException("indexName、indexMapping不能为空");
        }
        CreateIndexRequest request =  new CreateIndexRequest(indexInfo.getIndexName());
        request.settings(indexInfo.getSettings());
        request.mapping(indexInfo.getMappings());
        try {
            CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
            if (createIndexResponse.isAcknowledged()){
                return R.ok(true,"创建索引成功");
            }
        } catch (IOException e) {
            log.error("调用es创建索引异常：{}",e);
            throw new RuntimeException("创建索引失败");
        }

        return R.fail(false,"创建索引失败");
    }

    @Override
    public IndexInfo byIndexName(String indexName) {

        if (StringUtils.isEmpty(indexName)) {
            throw new ServiceException("indexName不能为空");
        }

        GetIndexRequest indexRequest = new GetIndexRequest(indexName);
        IndexInfo indexInfo = new IndexInfo();
        try {
            boolean exists = restHighLevelClient.indices().exists(indexRequest, RequestOptions.DEFAULT);
            if (exists==false){
                throw new RuntimeException("indexName不存在");
            }
            //提交构建
            GetIndexResponse getIndexResponse = restHighLevelClient.indices().get(indexRequest, RequestOptions.DEFAULT);
            //根据索引查询settings
            Settings settings = getIndexResponse.getSettings().get(indexName);
            //根据索引查询mapping
            MappingMetaData mappingMetaData = getIndexResponse.getMappings().get(indexName);

            Object json = JSON.toJSON(mappingMetaData);
            String s =settings.toString();
            HashMap<String, Object> map = new HashMap<>();
            map.put("settings",s);
            HashMap<String, Object> map2 = new HashMap<>();
            map2.put("mapping",json);
            indexInfo.setSettings(map);
            indexInfo.setMappings(map2);

            return indexInfo;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return indexInfo;
    }


    @Override
    public void indexDelete(String indexName) {
        if(StringUtils.isEmpty(indexName)){
            throw new RuntimeException("indexName不能为空");
        }
        try {
            GetIndexRequest indexRequest = new GetIndexRequest(indexName);
            boolean exists = restHighLevelClient.indices().exists(indexRequest, RequestOptions.DEFAULT);
            if(!exists){
                throw new RuntimeException("索引不存在");
            }
            DeleteIndexRequest request = new DeleteIndexRequest(indexName);
            restHighLevelClient.indices().delete(request,RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException("删除失败");
        }
    }


    //根据索引修改es
    @Override
    public IndexInfo updIndex(IndexInfo indexInfo) {

        // 检查旧索引是否存在
        GetIndexRequest getRequest = new GetIndexRequest(indexInfo.getIndexName());

        IndexInfo indexInfo1 = new IndexInfo();

           CreateIndexRequest requestIndexInfo =  new CreateIndexRequest(indexInfo1.getIndexName());

        try {
            boolean exists = restHighLevelClient.indices().exists(getRequest, RequestOptions.DEFAULT);
            if(!exists){
                throw new RuntimeException("索引不存在");
            }

            GetIndexResponse getIndexResponse = restHighLevelClient.indices().get(getRequest, RequestOptions.DEFAULT);
            MappingMetaData mappingMetaData = getIndexResponse.getMappings().get(indexInfo.getIndexName());
            Object json = JSON.toJSON(mappingMetaData);
            HashMap<String, Object> map2 = new HashMap<>();
            map2.put("mapping",json);
            indexInfo1.setIndexName(indexInfo.getIndexName());
            indexInfo1.setMappings(map2);

            DeleteIndexRequest request = new DeleteIndexRequest(indexInfo.getIndexName());
            restHighLevelClient.indices().delete(request,RequestOptions.DEFAULT);

            //提交构建
             restHighLevelClient.indices().create(requestIndexInfo,RequestOptions.DEFAULT);

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return indexInfo1;
    }

    @Override
    public void updateMapping(IndexInfo indexInfo, Integer flag) {

        if (StringUtils.isEmpty(indexInfo.getIndexName())){
            throw new ServiceException("indexName不能为空");
        }

        if (indexInfo.getMappings().isEmpty() && indexInfo.getSettings().isEmpty()) {
            throw new RuntimeException("请至少更新映射或配置之一");
        }

        // 根据索引名称查询mapping映射
        IndexInfo  oldIndex = indexGetSettingAndMapping(indexInfo.getIndexName());

        // 从传入的参数中取出 mapping 的 properties
        Map requestProperties = (Map) indexInfo.getMappings().get("properties");
        Map properties = new HashMap();
        if (requestProperties != null) {
            // 根据索引名取出 索引原本存在的 mapping 的 properties
            properties = (Map) oldIndex.getMappings().get("properties");
        }

        // 从传入的参数中取出 setting
        Map requestSettings = indexInfo.getSettings();
        Map settings = new HashMap();
        if (requestSettings != null) {
            // 根据索引名取出 索引原本存在的 setting
            settings = oldIndex.getSettings();
        }

        // 传来一个标记，如果为 1 代表要删除字段映射 否则 代表修改或添加映射
        if (flag == 1) {
            // 获取请求发送的映射的所有字段名
            Set mappingsKey = requestProperties.keySet();
            // 从原本的映射中移除传入的字段名
            properties.keySet().removeAll(mappingsKey);

            // 获取请求发送的配置的所有配置名
            Set settingsKey = requestSettings.keySet();
            // 从原本的配置中移除传入的配置名
            settings.keySet().removeAll(settingsKey);
        }

        if (requestProperties != null) {
            // 重新给properties赋值
            indexInfo.getMappings().put("properties", properties);
            // 将传入的properties字段名放入 根据索引名称查询到的 properties 中 (如果已有该字段，则会覆盖原先的值，以此来达到修改的效果)
            properties.putAll(requestProperties);
        }

        if (requestSettings != null) {
            // 给settings赋值
            indexInfo.getSettings().putAll(requestSettings);
        }

        // es不支持直接删除映射，所以需要删除索引重新创建索引映射
        indexDelete(indexInfo.getIndexName());

        try {
            // 重新创建索引
            indexCreate(indexInfo);
        } catch (Exception e) {
            // 报错后重新按原本的索引配置创建索引，避免到最后一步报错导致原本索引被删除无法恢复
            indexCreate(oldIndex);
            throw new RuntimeException(e);
        }

    }

    @Override
    public IndexInfo indexGetSettingAndMapping(String indexName) {
        if (StringUtils.isEmpty(indexName)) {
            throw new ServiceException("indexName不能为空");
        }

        GetMappingsRequest mappingsRequest = new GetMappingsRequest().indices(indexName);
        GetSettingsRequest settingsRequest = new GetSettingsRequest().indices(indexName);


        IndexInfo index = new IndexInfo();
        try {
            // 获取mapping
            GetMappingsResponse mappingsResponse = restHighLevelClient.indices().getMapping(mappingsRequest, RequestOptions.DEFAULT);
            Map<String, MappingMetaData> mappings = mappingsResponse.mappings();
            MappingMetaData mappingMetaData = mappings.get(indexName);
            Map<String, Object> MappingsMap = mappingMetaData.sourceAsMap();

            // 获取setting
            GetSettingsResponse settingsResponse = restHighLevelClient.indices().getSettings(settingsRequest, RequestOptions.DEFAULT);
            Settings settings = settingsResponse.getIndexToSettings().get(indexName);
            HashMap<String, Object> settingsMap = new HashMap<>();
            if (settings != null){
                settings.keySet().forEach(settingKey -> {
                    String settingValue = settings.get(settingKey);
                    settingsMap.put(settingKey, settingValue);
                });
            }
            index.setIndexName(indexName);
            index.setMappings(MappingsMap);
            index.setSettings(settingsMap);
            return index;
        } catch (Exception e) {
            log.error("根据索引名称获取索引信息异常:{}", e);
            throw new RuntimeException("根据索引名称获取Mapping异常");
        }

    }

    @Override
    public List<Map<String, Object>> byImages(Integer productId) {


        List<Map<String, Object>> maps = new ArrayList<>();
        // 执行搜索请求
        try {
            SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();


           //对于传参判空
            if(StringUtils.isBlank(String.valueOf(productId))){
                throw new RuntimeException("商品id不能为空!");
            }

            //查询商品spu
            boolQueryBuilder.must(QueryBuilders.matchQuery("productId", productId));
            searchSourceBuilder.query(boolQueryBuilder);


            // 在搜索时，应该从每个匹配的文档中包含源字段，并且只返回"image"这个字段，其他字段将被忽略。
            FetchSourceContext fetchSourceContext = new FetchSourceContext(true, new String[]{"image"}, null);
            searchSourceBuilder.fetchSource(fetchSourceContext);
            searchRequest.source(searchSourceBuilder);
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            // 处理响应
            SearchHit[] hits1 = searchResponse.getHits().getHits();

            //循环所查的数据 得到每个sku 的image 信息
            for (SearchHit documentFields : hits1) {
                Map<String, Object> sourceAsMap = documentFields.getSourceAsMap();
                maps.add(sourceAsMap);
            }

//            StringBuilder sb = new StringBuilder();
//            // 打印所有匹配的name属性值
//            for (Map<String, Object> source : maps) {
//                Object name = source.get("name");
//                if (name != null) {
//                    sb.append(name);
//                }
//            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return maps;
    }

}
